/** @file uart.c
 *
 * @author cameris camerisdump@gmail.com
 */

#include "uart.h"
#include <util/atomic.h>

/* keycodes */
#define XON  0x11 /**< @brief XON (DC1) hex representation */
#define XOFF 0x13 /**< @brief XOFF (DC3) hex representation */

/** @brief enable send interrupt */
#define SET_UDRIE() (UART_CONTROL1 |=  (1<<UDRIE_EN))

/** @brief disable send interrupt */
#define CLR_UDRIE() (UART_CONTROL1 &= ~(1<<UDRIE_EN))

/** @brief flow control state */
struct uart_flow{
	unsigned int stop:1; /**< @brief XOFF is active */
	unsigned int forced:1; /**< @brief indicates flow control has been set
							 manually and should not be automatically enabled
							 when buffer is empty enough */
};

/** @name transmit buffer variables
 * @{ */
static uint8_t uart_tx_in;
static uint8_t uart_tx_out;
static volatile uint8_t uart_tx_size;
static volatile struct uart_flow uart_tx_flow;
static uint8_t uart_tx_buf[UART_TX_BUF_SIZE];
/**@} */

/** @name receive buffer variables
 * @{ */
static uint8_t uart_rx_in;
static uint8_t uart_rx_out;
static volatile uint8_t uart_rx_size;
static volatile struct uart_flow uart_rx_flow;
static uint8_t uart_rx_buf[UART_RX_BUF_SIZE];
static uint8_t byte_valid;
/**@} */

/** @brief set baud rate to 2400 */
static void uart_2400(void)
{ /* {{{ */
#undef BAUD
#define BAUD 2400
#include <util/setbaud.h>
	BAUD_HI =  UBRRH_VALUE;
	BAUD_LO =  UBRRL_VALUE;
#if USE_2X
	UART_STATUS |= (1<<U2X_EN);
#else
	UART_STATUS &= ~(1<<U2X_EN);
#endif
} /* }}} */

/** @brief set baud rate to 4800 */
static void uart_4800(void)
{ /* {{{ */
#undef BAUD
#define BAUD 4800
#include <util/setbaud.h>
	BAUD_HI =  UBRRH_VALUE;
	BAUD_LO =  UBRRL_VALUE;
#if USE_2X
	UART_STATUS |= (1<<U2X_EN);
#else
	UART_STATUS &= ~(1<<U2X_EN);
#endif
} /* }}} */

/** @brief set baud rate to 9600 */
static void uart_9600(void)
{ /* {{{ */
#undef BAUD
#define BAUD 9600
#include <util/setbaud.h>
	BAUD_HI =  UBRRH_VALUE;
	BAUD_LO =  UBRRL_VALUE;
#if USE_2X
	UART_STATUS |= (1<<U2X_EN);
#else
	UART_STATUS &= ~(1<<U2X_EN);
#endif
} /* }}} */

/** @brief set baud rate to 19200 */
static void uart_19200(void)
{ /* {{{ */
#undef BAUD
#define BAUD 19200
#include <util/setbaud.h>
	BAUD_HI =  UBRRH_VALUE;
	BAUD_LO =  UBRRL_VALUE;
#if USE_2X
	UART_STATUS |= (1<<U2X_EN);
#else
	UART_STATUS &= ~(1<<U2X_EN);
#endif
} /* }}} */

/** @brief set baud rate to 38400 */
static void uart_38400(void)
{ /* {{{ */
#undef BAUD
#define BAUD 38400
#include <util/setbaud.h>
	BAUD_HI =  UBRRH_VALUE;
	BAUD_LO =  UBRRL_VALUE;
#if USE_2X
	UART_STATUS |= (1<<U2X_EN);
#else
	UART_STATUS &= ~(1<<U2X_EN);
#endif
} /* }}} */

/** @brief set baud rate to 57600 */
static void uart_57600(void)
{ /* {{{ */
#undef BAUD
#define BAUD 57600
#include <util/setbaud.h>
	BAUD_HI =  UBRRH_VALUE;
	BAUD_LO =  UBRRL_VALUE;
#if USE_2X
	UART_STATUS |= (1<<U2X_EN);
#else
	UART_STATUS &= ~(1<<U2X_EN);
#endif
} /* }}} */

/** @brief set baud rate to 115200 */
static void uart_115200(void)
{ /* {{{ */
#undef BAUD
#define BAUD 115200
#include <util/setbaud.h>
	BAUD_HI =  UBRRH_VALUE;
	BAUD_LO =  UBRRL_VALUE;
#if USE_2X
	UART_STATUS |= (1<<U2X_EN);
#else
	UART_STATUS &= ~(1<<U2X_EN);
#endif
} /* }}} */

/** @brief inserts byte at the first position in the send-queue
 * @param data byte to send */
static void uart_send_flow(uint8_t data)
{ /* {{{ */
	if (uart_tx_size < UART_TX_BUF_SIZE) {
		ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
			if (uart_tx_out == 0) {
				uart_tx_out = UART_TX_BUF_SIZE-1;
			} else {
				uart_tx_out--;
			}
			uart_tx_size++;
			uart_tx_buf[uart_tx_out] = data;
		}
	}
	/* enable UDRE interrupt */
	if (uart_tx_flow.stop == 0) SET_UDRIE();
} /* }}} */

/** @brief set communication parameters
 * @param para parameters to set */
void uart_set_parameters(struct uart_para *para)
{ /* {{{ */
	ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
		switch(para->baud) {
		    case BAUD_2400: uart_2400(); break;
		    case BAUD_4800: uart_4800(); break;
		    case BAUD_9600: uart_9600(); break;
		    case BAUD_19200: uart_19200(); break;
		    case BAUD_38400: uart_38400(); break;
		    case BAUD_57600: uart_57600(); break;
		    case BAUD_115200: uart_115200(); break;
		    default:
			    uart_9600();
			    para->baud = BAUD_9600;
			    break;
		}
		/*UCSRC = (1<<URSEL)|(para->parity<<UPM0)|(para->stopbits<<USBS)*/
				/*|(para->databits<<UCSZ0);*/
		/*TEST */
		UART_CONTROL2 = (para->parity<<PARITY_START)|(para->stopbits<<STOPBIT)|(para->databits<<DATABIT_START);
	}
} /* }}} */

/** @brief increase the baud rate one step 
 * @param para parameters to set */
void uart_inc_baud(struct uart_para *para)
{ /* {{{ */
	para->baud += 1;
	if (para->baud >= MAX_BAUD) {
		para->baud = MAX_BAUD -1;
	}
	uart_set_parameters(para);
} /* }}} */

/** @brief decrease the baud rate one step 
 * @param para parameters to set */
void uart_dec_baud(struct uart_para *para)
{ /* {{{ */
	if (para->baud != 0) {
		para->baud -= 1;
	}
	uart_set_parameters(para);
} /* }}} */

/** @brief increase the databits one step 
 * @param para parameters to set */
void uart_next_databits(struct uart_para *para)
{ /* {{{ */
	if (para->databits == 3) {
		para->databits = 0;
	} else {
		para->databits += 1;
	}
	uart_set_parameters(para);
} /* }}} */

/** @brief switch to next parity available
 * @param para parameters to set */
void uart_next_parity(struct uart_para *para)
{ /* {{{ */
	/* 0b01 is reserved, see datasheet */
	if (para->parity == 0) para->parity += 1;
	if (para->parity == 3) {
		para->parity = 0;
	} else {
		para->parity += 1;
	}
	uart_set_parameters(para);
} /* }}} */

/** @brief switch between 1 or 2 stopbits
 * @param para parameters to set */
void uart_toggle_stopbits(struct uart_para *para)
{ /* {{{ */
	para->stopbits ^= 1;
	uart_set_parameters(para);
} /* }}} */

/** @brief initialize uart buffer variables and interrupts */
void uart_init(void)
{ /* {{{ */
	/* Enable receiver and transmitter, receiver interupt */
	/*UCSRB = (1<<RXEN|1<<TXEN|1<<RXCIE);*/
	/*TEST*/
	UART_CONTROL1 = (1<<RX_EN)|(1<<TX_EN)|(1<<RXCIE_EN);

	byte_valid = 0;
	uart_rx_in = uart_rx_out = uart_rx_size = 0;
	uart_rx_flow.stop = uart_rx_flow.forced = 0;
	uart_tx_in = uart_tx_out = uart_tx_size = 0;
	uart_tx_flow.stop = uart_tx_flow.forced = 0;

	uart_send_flow(XON);
} /* }}} */

/** @brief put a string in the send-queue
 * @param str pointer to string */
void uart_send_string(uint8_t *str)
{ /* {{{ */
	while (*str) {
		uart_send_byte(*str);
		str++;
	}
} /* }}} */

/** @brief appends a byte to the send-queue, waits if it is full
 * @param data byte to send */
void uart_send_byte(uint8_t data)
{ /* {{{ */
	/* ensure interrupts are enabled when entering the loop */
	NONATOMIC_BLOCK(NONATOMIC_RESTORESTATE) {
		/* check if free space in buffer, but leave space for
		 * sending flow control char */
		while(uart_tx_size >= UART_TX_BUF_SIZE-1) {};
	}
	ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
		uart_tx_buf[uart_tx_in] = data;
		if (++uart_tx_in >= UART_TX_BUF_SIZE) {
			uart_tx_in = 0;
		}
		uart_tx_size++;
	}
	/* enable UDRE interrupt */
	if (uart_tx_flow.stop == 0) SET_UDRIE();
} /* }}} */

/** @brief sends XOFF to the receiver, also disables automatic enable */
void uart_stop_receive(void)
{ /* {{{ */
	ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
		if (uart_rx_flow.stop == 0 ) {
			uart_rx_flow.stop = 1;
			uart_send_flow(XOFF);
		}
		uart_rx_flow.forced = 1;
	}
} /* }}} */

/** @brief sends XON to the receiver, also re-enables automatic enable */
void uart_start_receive(void)
{ /* {{{ */
	ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
		if (uart_rx_flow.stop == 1 ) {
			uart_rx_flow.stop = 0;
			uart_send_flow(XON);
		}
		uart_rx_flow.forced = 0;
	}
} /* }}} */

/** @brief get a byte from the receive-queue
 *
 * get a byte from the receive-queue, sends XON to the receiver if buffer is
 * empty and not manually stopped. use uart_byte_is_valid() to  see if 0 has
 * actually been received or buffer is just empty
 * @return byte received byte */
uint8_t uart_get_byte(void)
{ /* {{{ */
	uint_fast8_t ret = 0;
	byte_valid = 0;
	ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
		if (uart_rx_size > 0) {
			ret = uart_rx_buf[uart_rx_out];
			byte_valid = 1;
			if (++uart_rx_out >= UART_RX_BUF_SIZE) {
				uart_rx_out = 0;
			}
			uart_rx_size--;
		}
		if ((uart_rx_flow.stop == 1) && (uart_rx_flow.forced == 0) &&
				(uart_rx_size < UART_RX_FLOW_START)) {
			uart_rx_flow.stop = 0;
			uart_send_flow(XON);
		}
	}
	return ret;
} /* }}} */

/** @brief check if received byte is valid
 *
 * indicates if the byte from uart_get_byte() has been received or
 * buffer is just empty. needed to differentiate an received 0 from 0 returned
 * because receive buffer is empty
 * @return 1 if byte has been received, 0 otherwise */
uint8_t uart_byte_is_valid(void)
{
	return byte_valid;
}

/** @brief puts a byte in the receive-queue,
 *
 * puts a byte in the receive-queue, sends XOFF to the
 * receiver if buffer is nearly full
 * @param data byte to put in queue */
void uart_put_recv_byte(uint8_t data)
{ /* {{{ */
	ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
		if (uart_rx_size < UART_RX_BUF_SIZE) {
			uart_rx_buf[uart_rx_in] = data;
			if (++uart_rx_in >= UART_RX_BUF_SIZE) {
				uart_rx_in = 0;
			}
			uart_rx_size++;
		}

		if ((uart_rx_size > UART_RX_FLOW_STOP) &&
				(uart_rx_flow.stop == 0)) {
			uart_rx_flow.stop = 1;
			uart_send_flow(XOFF);
		}
	}
} /* }}} */

/** @brief interrupt handling the sending of bytes in the send-queue */
ISR(UART_TX_INT_VECT)
{ /* {{{ */
	if (uart_tx_size > 0) {
		UART_DATA = uart_tx_buf[uart_tx_out];
		if (++uart_tx_out >= UART_TX_BUF_SIZE) {
			uart_tx_out = 0;
		}
		uart_tx_size--;
	} else {
		/* disable UDRE interrupt */
		CLR_UDRIE();
	}
} /* }}} */

/** @brief interrupt handling the receiving of bytes and flow control
 * from the receiver */
ISR(UART_RX_INT_VECT)
{ /* {{{ */
	uint8_t c;
	c = UART_DATA;
	if (c == XON) {
		uart_tx_flow.stop = 0;
		SET_UDRIE();
	} else if (c == XOFF) {
		uart_tx_flow.stop = 1;
		CLR_UDRIE();
	}
	uart_put_recv_byte(c);
} /* }}} */

/* vim:set foldmethod=marker: */
